<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="杰克小麻雀的博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    彻底弄懂Python标准库源码（一）—— os模块 |  半亩方塘
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

</head>

</html>
<body>
  <div id="app">
    
      
      <canvas width="1777" height="841"
        style="position: fixed; left: 0px; top: 0px; z-index: 99999; pointer-events: none;"></canvas>
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-彻底弄懂Python标准库源码（一）—— os模块"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  彻底弄懂Python标准库源码（一）—— os模块
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/06/15/%E5%BD%BB%E5%BA%95%E5%BC%84%E6%87%82Python%E6%A0%87%E5%87%86%E5%BA%93%E6%BA%90%E7%A0%81%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20os%E6%A8%A1%E5%9D%97/" class="article-date">
  <time datetime="2020-06-15T09:23:15.000Z" itemprop="datePublished">2020-06-15</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/Python/">Python</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">9k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">38 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <blockquote>
<p>作者: 杰克小麻雀<br>原文链接: <a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/106755148">https://blog.csdn.net/yushuaigee/article/details/106755148</a></p>
</blockquote>
<p><strong>目录</strong></p>
<ul>
<li><p><a href="#%E7%AC%AC1~22%E8%A1%8C%20%E6%A8%A1%E5%9D%97%E6%95%B4%E4%BD%93%E6%B3%A8%E9%87%8A%E3%80%81nt%E4%B8%8Eposix">第1~22行 模块整体注释、nt与posix</a></p>
</li>
<li><p><a href="#%E7%AC%AC24~46%E8%A1%8C%20%E6%A8%A1%E5%9D%97%E5%BC%95%E5%85%A5%E3%80%81_exists%E6%96%B9%E6%B3%95%E3%80%81_get_exports_list%E6%96%B9%E6%B3%95">第24~46行 模块引入、_exists方法、_get_exports_list方法</a></p>
</li>
<li><p><a href="#%E7%AC%AC48~97%E8%A1%8C%20%E6%A0%B9%E6%8D%AE%E7%B3%BB%E7%BB%9F%E4%B8%8D%E5%90%8C%E5%AF%BC%E5%85%A5%E4%B8%8D%E5%90%8C%E7%9A%84%E6%96%B9%E6%B3%95%E5%92%8C%E5%B1%9E%E6%80%A7">第48~97行 根据系统不同导入不同的方法和属性</a></p>
</li>
<li><p><a href="#%E7%AC%AC100~185%E8%A1%8C%20?%5B1%5D">第100~185行 ?[1]</a></p>
</li>
<li><p><a href="#%E7%AC%AC188~193%E8%A1%8C%C2%A0%E5%AE%9A%E4%B9%89%E4%B8%89%E4%B8%AA%E6%9E%9A%E4%B8%BE%E5%8F%98%E9%87%8F">第188~193行 定义三个枚举变量</a></p>
</li>
<li><p><a href="#%E7%AC%AC195~228%E8%A1%8C%20makedirs%E2%80%94%E2%80%94%E5%88%9B%E5%BB%BA%E5%A4%9A%E7%BA%A7%E7%9B%AE%E5%BD%95">第195~228行 makedirs——创建多级目录</a></p>
</li>
<li><p><a href="#%E7%AC%AC230~250%E8%A1%8C%20removedirs%E2%80%94%E2%80%94%E5%88%A0%E9%99%A4%E5%A4%9A%E7%BA%A7%E7%9B%AE%E5%BD%95">第230~250行 removedirs——删除多级目录</a></p>
</li>
<li><p><a href="#%E7%AC%AC252~278%E8%A1%8C%20renames%E2%80%94%E2%80%94%E9%87%8D%E5%91%BD%E5%90%8D%E7%9B%AE%E5%BD%95%E6%88%96%E6%96%87%E4%BB%B6">第252~278行 renames——重命名目录或文件</a></p>
</li>
<li><p><a href="#%E7%AC%AC280~421%E8%A1%8C%20walk%E2%80%94%E2%80%94%E7%9B%AE%E5%BD%95%E6%A0%91%E7%94%9F%E6%88%90%E5%99%A8">第280~421行 walk——目录树生成器</a></p>
</li>
<li><p><a href="#%E6%9C%AA%E5%AE%8C%E5%BE%85%E7%BB%AD%E2%80%A6%E2%80%A6">未完待续……</a></p>
</li>
</ul>
<p>os模块包含了一些与操作系统相关的函数接口，而且它是支持跨平台的，它封装了 nt.py(windows) 和 posix.py (类Unix)两个模块的接口，而后面两个模块是由C语言实现的、直接和系统交互的底层接口。也就是说os模块能够处理平台间的差异问题，使得编写好的程序无需做任何改动就能在不同的平台上运行。如果想要查看os模块的所有内容，可以使用<code>dir(os)</code>方法查看。</p>
<p><img src="https://cdn.jsdelivr.net/gh/yushuaige/myblog@master/img/20200529172306852.png" alt="20200529172306852"></p>
<p>可以看到os模块中有这么多属性和方法，这些都是可以通过“os.”访问的。因为os模块是使用纯Python实现的标准库，所以在Python安装目录中也可以找到os模块的源码。打开Python安装目录下 \Lib\os.py 文件，或者源码目录下 \Lib\os.py 文件，就可以查看os模块的源码了。整个代码看下来，os.py 主要是将底层接口进行了一层封装，不知道其他标准库是不是也是这样。</p>
<p>以下标题中的行数是与我所用的3.8.4版本os.py文件真实的行数对应的，而分析文字部分所说的行数，是对应截取的代码段的行数，这样比较方便看。</p>
<h3 id="第1-22行-模块整体注释、nt与posix"><a href="#第1-22行-模块整体注释、nt与posix" class="headerlink" title="第1~22行 模块整体注释、nt与posix"></a>第1~22行 模块整体注释、nt与posix</h3><p>首先是第1行到第22行，这是一段整个模块的注释。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">r&quot;&quot;&quot;OS routines for NT or Posix depending on what system we&#x27;re on.</span></span><br><span class="line"><span class="string">This exports:</span></span><br><span class="line"><span class="string">  - all functions from posix or nt, e.g. unlink, stat, etc.</span></span><br><span class="line"><span class="string">  - os.path is either posixpath or ntpath</span></span><br><span class="line"><span class="string">  - os.name is either &#x27;posix&#x27; or &#x27;nt&#x27;</span></span><br><span class="line"><span class="string">  - os.curdir is a string representing the current directory (always &#x27;.&#x27;)</span></span><br><span class="line"><span class="string">  - os.pardir is a string representing the parent directory (always &#x27;..&#x27;)</span></span><br><span class="line"><span class="string">  - os.sep is the (or a most common) pathname separator (&#x27;/&#x27; or &#x27;\\&#x27;)</span></span><br><span class="line"><span class="string">  - os.extsep is the extension separator (always &#x27;.&#x27;)</span></span><br><span class="line"><span class="string">  - os.altsep is the alternate pathname separator (None or &#x27;/&#x27;)</span></span><br><span class="line"><span class="string">  - os.pathsep is the component separator used in $PATH etc</span></span><br><span class="line"><span class="string">  - os.linesep is the line separator in text files (&#x27;\r&#x27; or &#x27;\n&#x27; or &#x27;\r\n&#x27;)</span></span><br><span class="line"><span class="string">  - os.defpath is the default search path for executables</span></span><br><span class="line"><span class="string">  - os.devnull is the file path of the null device (&#x27;/dev/null&#x27;, etc.)</span></span><br><span class="line"><span class="string">Programs that import and use &#x27;os&#x27; stand a better chance of being</span></span><br><span class="line"><span class="string">portable between different platforms.  Of course, they must then</span></span><br><span class="line"><span class="string">only use functions that are defined by all platforms (e.g., unlink</span></span><br><span class="line"><span class="string">and opendir), and leave all pathname manipulation to os.path</span></span><br><span class="line"><span class="string">(e.g., split and join).</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br></pre></td></tr></table></figure>
<p>这段注释解释了OS模块的功能，就是为了支持跨平台的移植代码，根据不同的系统调用对应平台支持的接口。以前也写过跨Windows和Linux的Python程序，当时就是在网上查的，根据os.name的值是字符串’nt’还是’posix’来判断当前是哪种系统，然后走到对应的代码分支，并没有深究就是为什么。这次研究一下什么是NT和Posix：</p>
<p>NT即Windows NT，就是Windows系统的内核，其中的NT意为New Technology。微软一开始命名系统是以内核版本命名的，比如：Microsoft Windows NT 3.1 (1993)、Microsoft Windows NT 3.5 (1994)，知道Windows 2000之后才改成现在的xp、7、Vista、10这种。它的内核版本还是在不断演进的，只是系统对外发布的命名风格发生了变化。所以OS模块中用‘nt’代表Windows系统。</p>
<p>POSIX提供了一套大体上基于Unix的可移植操作系统标准，意在期望获得源代码级别的软件可移植性。为了实现相同的功能，在不同的系统上可能有着不同的接口名字，写代码的时候需要根据不通系统在源代码级别上进行适配。为了解决这个问题，让不同系统都遵循POSIX标准，就是在原来的接口的基础上再封装一层，起一个通用的名字，这样就可以用一份源代码运行在不同的系统上，这个道理和OS模块的功能是一样的。Linux是与POSIX兼容的(现在Window也开始支持这个标准了)，所以OS模块中用‘posix’代表Linux这种类UNIX系统。</p>
<p>为什么选择这两个单词我们不用深究，只是一种规定，我们用的时候只需’判断os.name == ‘nt是Windows系统，os.name == ‘posix’是Linux或Mac系统就行了，至于OS怎么根据‘nt’和‘posix’两个字符串实现区分不同系统的，这个在源码中有体现，后面马上就能看的到。</p>
<h3 id="第24-46行-模块引入、-exists方法、-get-exports-list方法"><a href="#第24-46行-模块引入、-exists方法、-get-exports-list方法" class="headerlink" title="第24~46行 模块引入、_exists方法、_get_exports_list方法"></a>第24~46行 模块引入、_exists方法、_get_exports_list方法</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#&#x27;</span></span><br><span class="line"><span class="keyword">import</span> abc</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> stat <span class="keyword">as</span> st</span><br><span class="line"> </span><br><span class="line"><span class="keyword">from</span> _collections_abc <span class="keyword">import</span> _check_methods</span><br><span class="line"> </span><br><span class="line">_names = sys.builtin_module_names</span><br><span class="line"> </span><br><span class="line"><span class="comment"># Note:  more names are added to __all__ later.</span></span><br><span class="line">__all__ = [<span class="string">&quot;altsep&quot;</span>, <span class="string">&quot;curdir&quot;</span>, <span class="string">&quot;pardir&quot;</span>, <span class="string">&quot;sep&quot;</span>, <span class="string">&quot;pathsep&quot;</span>, <span class="string">&quot;linesep&quot;</span>,</span><br><span class="line">           <span class="string">&quot;defpath&quot;</span>, <span class="string">&quot;name&quot;</span>, <span class="string">&quot;path&quot;</span>, <span class="string">&quot;devnull&quot;</span>, <span class="string">&quot;SEEK_SET&quot;</span>, <span class="string">&quot;SEEK_CUR&quot;</span>,</span><br><span class="line">           <span class="string">&quot;SEEK_END&quot;</span>, <span class="string">&quot;fsencode&quot;</span>, <span class="string">&quot;fsdecode&quot;</span>, <span class="string">&quot;get_exec_path&quot;</span>, <span class="string">&quot;fdopen&quot;</span>,</span><br><span class="line">           <span class="string">&quot;popen&quot;</span>, <span class="string">&quot;extsep&quot;</span>]</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_exists</span>(<span class="params">name</span>):</span></span><br><span class="line">    <span class="keyword">return</span> name <span class="keyword">in</span> <span class="built_in">globals</span>()</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_get_exports_list</span>(<span class="params">module</span>):</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">list</span>(module.__all__)</span><br><span class="line">    <span class="keyword">except</span> AttributeError:</span><br><span class="line">        <span class="keyword">return</span> [n <span class="keyword">for</span> n <span class="keyword">in</span> <span class="built_in">dir</span>(module) <span class="keyword">if</span> n[<span class="number">0</span>] != <span class="string">&#x27;_&#x27;</span>]</span><br></pre></td></tr></table></figure>
<p> 第2行引入 abc 模块，在os这里主要用到里面的ABC，Abstract Base Class（抽象基类），主要定义了基本类和最基本的抽象方法，可以为子类定义共有的API，不需要具体实现。相当于是Java中的接口或者是抽象类。这个抽象基类的作用一两句话说不清楚，鉴于abc.py也属于Python标准库，下一篇就具体研究一下这个abc模块。</p>
<p>第3行引入sys模块，这是一个C实现的内置模块，主要是实现Python解释器、操作系统相关的操作。</p>
<p>第4行引入stat模块，这个模块主要实现文件状态检查之类的操作，也属于一个标准库，在os这里只是用到了 st.S_ISDIR 这个方法，判断是否是目录。</p>
<p>第6行引入_collections_abc模块的_check_methods方法，这个模块是用于集合的抽象基类，也属于一个标准库，_check_methods用来判断一个对象是否含有某个属性。</p>
<p>第8行 sys.builtin_module_names 返回一个包含内建模块名字的元组，包含所有已经编译到Python解释器的模块名字。这里就可以解释os模块怎么根据‘nt’和‘posix’两个字符串实现区分不同系统的：Window系统的Python会安装nt模块，这个返回的元组中就会包含‘nt’，而其他系统的Python在安装时不安装nt模块，而是安装posix模块，这个返回的元组中就会包含‘posix’。</p>
<p>下面一行是一个注释，提示：更多的名称会在后面慢慢加入到__all__中。</p>
<p>第11行，<strong>all</strong> 是针对模块公开接口的一种约定，以提供了”白名单“的形式暴露接口。如果定义了<strong>all__，其他文件中使用from xxx import *导入该文件时，只会导入 __all</strong> 列出的成员，其他成员都被排除在外。若没定义，则导入模块内的所有公有属性/方法和类 。因为只是一种约定，就像用__前缀表示私有成员一样，它只对import <code>*</code>起作用，对from xxx import xxx不起作用。 </p>
<p>下面定义了_exists方法，用于通过名字获得全局变量中的对象。其中global()方法是解释器内置方法，不需要导入就可以直接用，会以字典类型返回当前位置的全部全局变量。类似的还有locals()方法，后者以字典类型返回当前位置的局部变量。</p>
<p>再下面是_get_exports_list方法，这个了解了上面的__all__就很好理解，就是通过模块名获得对应模块对外暴露的接口，如果该模块没有定义__all__，即发生了AttributeError，就返回模块所有接口里面不是以_前缀开头的接口。可以看出这是在遵守约定。</p>
<h3 id="第48-97行-根据系统不同导入不同的方法和属性"><a href="#第48-97行-根据系统不同导入不同的方法和属性" class="headerlink" title="第48~97行 根据系统不同导入不同的方法和属性"></a>第48~97行 根据系统不同导入不同的方法和属性</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Any new dependencies of the os module and/or changes in path separator</span></span><br><span class="line"><span class="comment"># requires updating importlib as well.</span></span><br><span class="line"><span class="keyword">if</span> <span class="string">&#x27;posix&#x27;</span> <span class="keyword">in</span> _names:</span><br><span class="line">    name = <span class="string">&#x27;posix&#x27;</span></span><br><span class="line">    linesep = <span class="string">&#x27;\n&#x27;</span></span><br><span class="line">    <span class="keyword">from</span> posix <span class="keyword">import</span> *</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">from</span> posix <span class="keyword">import</span> _exit</span><br><span class="line">        __all__.append(<span class="string">&#x27;_exit&#x27;</span>)</span><br><span class="line">    <span class="keyword">except</span> ImportError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">import</span> posixpath <span class="keyword">as</span> path</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">from</span> posix <span class="keyword">import</span> _have_functions</span><br><span class="line">    <span class="keyword">except</span> ImportError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"> </span><br><span class="line">    <span class="keyword">import</span> posix</span><br><span class="line">    __all__.extend(_get_exports_list(posix))</span><br><span class="line">    <span class="keyword">del</span> posix</span><br><span class="line"> </span><br><span class="line"><span class="keyword">elif</span> <span class="string">&#x27;nt&#x27;</span> <span class="keyword">in</span> _names:</span><br><span class="line">    name = <span class="string">&#x27;nt&#x27;</span></span><br><span class="line">    linesep = <span class="string">&#x27;\r\n&#x27;</span></span><br><span class="line">    <span class="keyword">from</span> nt <span class="keyword">import</span> *</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">from</span> nt <span class="keyword">import</span> _exit</span><br><span class="line">        __all__.append(<span class="string">&#x27;_exit&#x27;</span>)</span><br><span class="line">    <span class="keyword">except</span> ImportError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">import</span> ntpath <span class="keyword">as</span> path</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">import</span> nt</span><br><span class="line">    __all__.extend(_get_exports_list(nt))</span><br><span class="line">    <span class="keyword">del</span> nt</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">from</span> nt <span class="keyword">import</span> _have_functions</span><br><span class="line">    <span class="keyword">except</span> ImportError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="keyword">raise</span> ImportError(<span class="string">&#x27;no os specific module found&#x27;</span>)</span><br><span class="line"> </span><br><span class="line">sys.modules[<span class="string">&#x27;os.path&#x27;</span>] = path</span><br><span class="line"><span class="keyword">from</span> os.path <span class="keyword">import</span> (curdir, pardir, sep, pathsep, defpath, extsep, altsep,</span><br><span class="line">    devnull)</span><br><span class="line"> </span><br><span class="line"><span class="keyword">del</span> _names</span><br></pre></td></tr></table></figure>
<p>注释是说，os模块的任何新依赖关系、路径分隔符的更改都需要更新导入库。因此下面的 if 和 elif 代码段就是在根据不同的系统更新导入的库。</p>
<p>第3行就是上面说的根据当前已经编译到Python解释器的模块名字判断当前系统是不是遵循POSIX标准的系统(Mac、Linux等)，如果是就将新建属性 name 赋值 posix ，注意这里的属性是属于os模块的，这也是为什么我们在外面导入os模块后，就可以使用os.name来判断系统类型，和文件最开始的注释中的os.name就对上了，看了原理就会发现其实也没什么神奇的。同理，下一行的linesep对应文件最开始的注释中的os.linesep，表示文本文件的分隔符，在Mac、Linux等系统中默认是 ‘\n’。</p>
<p>第6行导入posix模块的全部内容。此模块提供了对基于 C 标准和 POSIX 标准（一种稍加修改的 Unix 接口）进行标准化的系统功能的访问。官方文档都用加粗字体说了：请勿直接导入此模块<strong>。</strong> 而应导入os模块，它提供了此接口的可移植版本，而且没有性能损失。前面说过os模块就是对posix模块和nt模块的封装，在Windows上安装的Python是没有posix模块的，所以IDE里也没法跳转过去，这里暂不深究。</p>
<p>下面五行尝试从 posix 导入 _exit 方法并将其加到<strong>all__中暴露出去，如果没有就pass。前面已经import *了，为什么还要再单独导入一次呢？我觉得这里是因为不确定 posix 里是否有此方法(可能POSIX标准的部分系统没有这个方法)，所以不能直接使用__all</strong>.append(‘_exit’)。</p>
<p>第12行将 posixpath 导入并改名为 path，所以我们平时使用的 os.path 其实不是os模块本身。posixpath 是另外一个用Python实现的专门处理关于路径的问题的库，这个库Windows系统上的Python也会安装，后面的文章再具体分析。</p>
<p>下面是尝试导入_have_functions，这是一个列表，里面包含对应系统所支持的函数名，后面会用到。再下面就是把posix模块对外暴露的方法和属性和当前os模块合并，其中 _get_exports_list 方法是前面刚刚定义的。</p>
<p>再往下看，elif 代码块和前面一模一样，只是把 posix 换成了 nt 。后面还有一个 else 代码块，如果 ‘posix’ 和 ‘nt’都不在内建属性列表中将会报错，说明 os 模块只支持 posix和nt 标准的系统(除了这两种估计也没啥别的标准了)。到这里，当前系统是什么类型已经很明确了，只需将对应系统的路径分隔符等导入进来。</p>
<h3 id="第100-185行-1"><a href="#第100-185行-1" class="headerlink" title="第100~185行 ?[1]"></a>第100~185行 ?[1]</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> _exists(<span class="string">&quot;_have_functions&quot;</span>):</span><br><span class="line">    _globals = <span class="built_in">globals</span>()</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_add</span>(<span class="params"><span class="built_in">str</span>, fn</span>):</span></span><br><span class="line">        <span class="keyword">if</span> (fn <span class="keyword">in</span> _globals) <span class="keyword">and</span> (<span class="built_in">str</span> <span class="keyword">in</span> _have_functions):</span><br><span class="line">            _set.add(_globals[fn])</span><br><span class="line"> </span><br><span class="line">    _set = <span class="built_in">set</span>()</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FACCESSAT&quot;</span>,  <span class="string">&quot;access&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FCHMODAT&quot;</span>,   <span class="string">&quot;chmod&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FCHOWNAT&quot;</span>,   <span class="string">&quot;chown&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FSTATAT&quot;</span>,    <span class="string">&quot;stat&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FUTIMESAT&quot;</span>,  <span class="string">&quot;utime&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_LINKAT&quot;</span>,     <span class="string">&quot;link&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_MKDIRAT&quot;</span>,    <span class="string">&quot;mkdir&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_MKFIFOAT&quot;</span>,   <span class="string">&quot;mkfifo&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_MKNODAT&quot;</span>,    <span class="string">&quot;mknod&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_OPENAT&quot;</span>,     <span class="string">&quot;open&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_READLINKAT&quot;</span>, <span class="string">&quot;readlink&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_RENAMEAT&quot;</span>,   <span class="string">&quot;rename&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_SYMLINKAT&quot;</span>,  <span class="string">&quot;symlink&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_UNLINKAT&quot;</span>,   <span class="string">&quot;unlink&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_UNLINKAT&quot;</span>,   <span class="string">&quot;rmdir&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_UTIMENSAT&quot;</span>,  <span class="string">&quot;utime&quot;</span>)</span><br><span class="line">    supports_dir_fd = _set</span><br><span class="line"> </span><br><span class="line">    _set = <span class="built_in">set</span>()</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FACCESSAT&quot;</span>,  <span class="string">&quot;access&quot;</span>)</span><br><span class="line">    supports_effective_ids = _set</span><br><span class="line"> </span><br><span class="line">    _set = <span class="built_in">set</span>()</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FCHDIR&quot;</span>,     <span class="string">&quot;chdir&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FCHMOD&quot;</span>,     <span class="string">&quot;chmod&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FCHOWN&quot;</span>,     <span class="string">&quot;chown&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FDOPENDIR&quot;</span>,  <span class="string">&quot;listdir&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FDOPENDIR&quot;</span>,  <span class="string">&quot;scandir&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FEXECVE&quot;</span>,    <span class="string">&quot;execve&quot;</span>)</span><br><span class="line">    _set.add(stat) <span class="comment"># fstat always works</span></span><br><span class="line">    _add(<span class="string">&quot;HAVE_FTRUNCATE&quot;</span>,  <span class="string">&quot;truncate&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FUTIMENS&quot;</span>,   <span class="string">&quot;utime&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FUTIMES&quot;</span>,    <span class="string">&quot;utime&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FPATHCONF&quot;</span>,  <span class="string">&quot;pathconf&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> _exists(<span class="string">&quot;statvfs&quot;</span>) <span class="keyword">and</span> _exists(<span class="string">&quot;fstatvfs&quot;</span>): <span class="comment"># mac os x10.3</span></span><br><span class="line">        _add(<span class="string">&quot;HAVE_FSTATVFS&quot;</span>, <span class="string">&quot;statvfs&quot;</span>)</span><br><span class="line">    supports_fd = _set</span><br><span class="line"> </span><br><span class="line">    _set = <span class="built_in">set</span>()</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FACCESSAT&quot;</span>,  <span class="string">&quot;access&quot;</span>)</span><br><span class="line">    <span class="comment"># Some platforms don&#x27;t support lchmod().  Often the function exists</span></span><br><span class="line">    <span class="comment"># anyway, as a stub that always returns ENOSUP or perhaps EOPNOTSUPP.</span></span><br><span class="line">    <span class="comment"># (No, I don&#x27;t know why that&#x27;s a good design.)  ./configure will detect</span></span><br><span class="line">    <span class="comment"># this and reject it--so HAVE_LCHMOD still won&#x27;t be defined on such</span></span><br><span class="line">    <span class="comment"># platforms.  This is Very Helpful.</span></span><br><span class="line">    <span class="comment">#</span></span><br><span class="line">    <span class="comment"># However, sometimes platforms without a working lchmod() *do* have</span></span><br><span class="line">    <span class="comment"># fchmodat().  (Examples: Linux kernel 3.2 with glibc 2.15,</span></span><br><span class="line">    <span class="comment"># OpenIndiana 3.x.)  And fchmodat() has a flag that theoretically makes</span></span><br><span class="line">    <span class="comment"># it behave like lchmod().  So in theory it would be a suitable</span></span><br><span class="line">    <span class="comment"># replacement for lchmod().  But when lchmod() doesn&#x27;t work, fchmodat()&#x27;s</span></span><br><span class="line">    <span class="comment"># flag doesn&#x27;t work *either*.  Sadly ./configure isn&#x27;t sophisticated</span></span><br><span class="line">    <span class="comment"># enough to detect this condition--it only determines whether or not</span></span><br><span class="line">    <span class="comment"># fchmodat() minimally works.</span></span><br><span class="line">    <span class="comment">#</span></span><br><span class="line">    <span class="comment"># Therefore we simply ignore fchmodat() when deciding whether or not</span></span><br><span class="line">    <span class="comment"># os.chmod supports follow_symlinks.  Just checking lchmod() is</span></span><br><span class="line">    <span class="comment"># sufficient.  After all--if you have a working fchmodat(), your</span></span><br><span class="line">    <span class="comment"># lchmod() almost certainly works too.</span></span><br><span class="line">    <span class="comment">#</span></span><br><span class="line">    <span class="comment"># _add(&quot;HAVE_FCHMODAT&quot;,   &quot;chmod&quot;)</span></span><br><span class="line">    _add(<span class="string">&quot;HAVE_FCHOWNAT&quot;</span>,   <span class="string">&quot;chown&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FSTATAT&quot;</span>,    <span class="string">&quot;stat&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_LCHFLAGS&quot;</span>,   <span class="string">&quot;chflags&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_LCHMOD&quot;</span>,     <span class="string">&quot;chmod&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> _exists(<span class="string">&quot;lchown&quot;</span>): <span class="comment"># mac os x10.3</span></span><br><span class="line">        _add(<span class="string">&quot;HAVE_LCHOWN&quot;</span>, <span class="string">&quot;chown&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_LINKAT&quot;</span>,     <span class="string">&quot;link&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_LUTIMES&quot;</span>,    <span class="string">&quot;utime&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_LSTAT&quot;</span>,      <span class="string">&quot;stat&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_FSTATAT&quot;</span>,    <span class="string">&quot;stat&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;HAVE_UTIMENSAT&quot;</span>,  <span class="string">&quot;utime&quot;</span>)</span><br><span class="line">    _add(<span class="string">&quot;MS_WINDOWS&quot;</span>,      <span class="string">&quot;stat&quot;</span>)</span><br><span class="line">    supports_follow_symlinks = _set</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">del</span> _set</span><br><span class="line">    <span class="keyword">del</span> _have_functions</span><br><span class="line">    <span class="keyword">del</span> _globals</span><br><span class="line">    <span class="keyword">del</span> _add</span><br></pre></td></tr></table></figure>
<p>这段代码的意思看懂了，但是没有明白它的作用。将一些 globals 里和 _have_functions 同时出现的方法名加到 一个set里，并重新赋值给supports_dir_fd、supports_effective_ids、supports_fd、supports_follow_symlinks四个集合。但是只有supports_dir_fd和supports_fd在后面的代码中用到了，另外两个集合整个代码里都没有用过，不知道是什么作用，先跳过，保留疑问[1]。</p>
<h3 id="第188-193行-定义三个枚举变量"><a href="#第188-193行-定义三个枚举变量" class="headerlink" title="第188~193行 定义三个枚举变量"></a>第188~193行 定义三个枚举变量</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Python uses fixed values for the SEEK_ constants; they are mapped</span></span><br><span class="line"><span class="comment"># to native constants if necessary in posixmodule.c</span></span><br><span class="line"><span class="comment"># Other possible SEEK values are directly imported from posixmodule.c</span></span><br><span class="line">SEEK_SET = <span class="number">0</span></span><br><span class="line">SEEK_CUR = <span class="number">1</span></span><br><span class="line">SEEK_END = <span class="number">2</span></span><br></pre></td></tr></table></figure>
<p>注释的意思是: Python对SEEK_常量使用固定值，如果需要，它们在 posixmodule.c 中被映射到本机常量。其他可能的SEEK_常量值直接从posixmodule.c 导入。</p>
<p>这三个常量一般用作fseek函数的一个入参。fseek函数是C语言中用于二进制方式打开的文件时，移动文件读写指针位置。原型是int fseek(FILE *stream, long offset, int fromwhere); 第一个参数stream为文件指针第，第二个参数offset为偏移量，整数表示正向偏移，负数表示负向偏移， 第三个参数设定从文件的哪里开始偏移,可能取值为：SEEK_SET： 文件开头；SEEK_CUR： 当前位置；EEK_END： 文件结尾。这三个变量我觉得不用深究，鉴于模块开头的 <strong>all</strong> 里也加入了这三个变量名，应该是在模块外面调用别的函数的时候作为入参使用的，这里定义一下可以起到枚举的作用。</p>
<h3 id="第195-228行-makedirs——创建多级目录"><a href="#第195-228行-makedirs——创建多级目录" class="headerlink" title="第195~228行 makedirs——创建多级目录"></a>第195~228行 makedirs——创建多级目录</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Super directory utilities.</span></span><br><span class="line"><span class="comment"># (Inspired by Eric Raymond; the doc strings are mostly his)</span></span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">makedirs</span>(<span class="params">name, mode=<span class="number">0o777</span>, exist_ok=<span class="literal">False</span></span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;makedirs(name [, mode=0o777][, exist_ok=False])</span></span><br><span class="line"><span class="string">    Super-mkdir; create a leaf directory and all intermediate ones.  Works like</span></span><br><span class="line"><span class="string">    mkdir, except that any intermediate path segment (not just the rightmost)</span></span><br><span class="line"><span class="string">    will be created if it does not exist. If the target directory already</span></span><br><span class="line"><span class="string">    exists, raise an OSError if exist_ok is False. Otherwise no exception is</span></span><br><span class="line"><span class="string">    raised.  This is recursive.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    head, tail = path.split(name)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> tail:</span><br><span class="line">        head, tail = path.split(head)</span><br><span class="line">    <span class="keyword">if</span> head <span class="keyword">and</span> tail <span class="keyword">and</span> <span class="keyword">not</span> path.exists(head):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            makedirs(head, exist_ok=exist_ok)</span><br><span class="line">        <span class="keyword">except</span> FileExistsError:</span><br><span class="line">            <span class="comment"># Defeats race condition when another thread created the path</span></span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">        cdir = curdir</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(tail, <span class="built_in">bytes</span>):</span><br><span class="line">            cdir = <span class="built_in">bytes</span>(curdir, <span class="string">&#x27;ASCII&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> tail == cdir:           <span class="comment"># xxx/newdir/. exists if xxx/newdir exists</span></span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        mkdir(name, mode)</span><br><span class="line">    <span class="keyword">except</span> OSError:</span><br><span class="line">        <span class="comment"># Cannot rely on checking for EEXIST, since the operating system</span></span><br><span class="line">        <span class="comment"># could give priority to other errors like EACCES or EROFS</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> exist_ok <span class="keyword">or</span> <span class="keyword">not</span> path.isdir(name):</span><br><span class="line">            <span class="keyword">raise</span></span><br></pre></td></tr></table></figure>
<p>前面大部分都是一些准备工作，到195行这里才刚刚开始正题。</p>
<p>首先是两行注释：关于目录的方法的超级版。(灵感来自Eric Raymond，the doc strings are mostly his(这句没搞明白啥意思，是文档的内容大部分是他的？))。埃里克·史蒂文·雷蒙德，著名的计算机程序员，开源软件运动的旗手。他是INTERCAL编程语言的主要创作者之一，曾经为EMACS编辑器作出贡献。雷蒙德还是著名的Fetchmail程序的作者。他还编写了一个最初用于Linux内核设置的设置程序——百度百科。</p>
<p>下面定义了 makedirs 方法，这是mkdir方法的超级版本，创建一个子目录和所有中间目录。它和mkdir的区别是：如果要在目录a 下新建一个 ‘a/b/c’ 的目录，makedirs 可以一次新建 b 并且在 b 下新建c，而mkdir 需要先新建 b 再进入到 b 下新建 c，需要调用两次。如果目标目录已经存在，并且 exist_ok 参数是 False，则引发 OSError，exist_ok 参数是 True 则不引发异常。这个函数是通过递归实现前面的功能的。它有3个入参, name 是字符串类型的目标路径名，mode 表示创建的目录的权限，默认值是777，该参数在windows下会被忽略，exist_ok 前面注释里说了，就是在目标路径存在的情况下是否报错。这里可以看出os模块也不是仅仅对nt或posix进行简单封装，还加入了一些更加方便的功能。</p>
<p>看代码要先找准代码的主体部分，然后再去看那些辅助部分。这个方法的主体部分就是该段代码的第19行 makedirs(head, exist_ok=exist_ok) 和第29行 mkdir(name, mode) ，其他是一些条件判断或者校验。</p>
<p>第14行调用 path.split 即 posixpath 或者 ntpath模块的 split 方法。我看了ntpath里的 split 方法，考虑了传入字符串的许多种格式，包括一些写错的情况，当前我们只需要知道它的作用是对传入路径进行分割，以最后一个路径分隔符作为分隔，head和tail。比如传入 ‘C:/ttt/eee/sss/ttt’，返回 head = ‘C:/ttt/eee/sss’，tail = ‘ttt’。下面15行判断 tail 如果为空会再执行一次 split，主要是考虑到传入的参数最后带了路径分隔符的情况，比如传入’C:/ttt/eee/sss/ttt/‘ ，第一次执行split 就会返回 head = ‘C:/ttt/eee/sss/ttt’，tail = ‘’。</p>
<p>第17行是说如果 head 和 tail 都不为空，而且 head已经存在的情况下，调用makedirs进行递归。这里注意递归时mode参数被省略了，也就是说如果创建的是多级目录，除了第一层是用传入的权限，其他子目录都是用的默认参数权限即777，当然这是针对非Windows系统来说的。</p>
<p>第20~22行，如果发生路径已存在的异常，就pass，注释说这样为了避免多线程创建路径的情况。</p>
<p>第23行是取curdir，这个在开头注释中有说明，代表当前目录的字符串，就是 “.” 。</p>
<p>第24~27行，是为了适配最后一个路径是 “.” 的情况，还考虑到路径编码是bytes类型的情况。一开始没看出来这段代码的必要性，于是我将这段代码注释，然后执行 os.makedirs(‘test/test/.’) ，结果出现异常如下图。</p>
<p><img src="https://cdn.jsdelivr.net/gh/yushuaige/myblog@master/img/20200611215702925.png" alt="img"></p>
<p>这是在第30行捕获到的OSError类型的异常，这说明调用底层接口 mkdir(name, mode) 时，对于路径 “.” ，系统会自动解析为当前路径(引发异常时的“当前路径”就是 “test/test” ，已经递归创建了 )，当前路径已经存在，再创建自然会引发异常。但是我想应该不会有人这样传参吧，另外我测了一下路径最后是两个点 “..” 的情况，原代码会直接报异常，毕竟不可能把所有异常入参都覆盖到，这也是我平时写代码比较纠结的地方：到底要不得要把能想到的所有异常都主动捕获并处理掉?</p>
<p>后面的代码就好理解了，经过前面的一系列处理和递归，到第29行时，name 参数已经变成一层目录了，直接调用 nt 或 posix 模块的 mkdir() 就可以创建一层目录了。当然可能会出现一些异常，但这里主要是捕获“路径已存在”类型的异常，注释是说这里之所以捕获 OSError 而不是 EEXISTError ，是因为有的系统发现路径已存在时不一定报已存在而会报其他错误。</p>
<h3 id="第230-250行-removedirs——删除多级目录"><a href="#第230-250行-removedirs——删除多级目录" class="headerlink" title="第230~250行 removedirs——删除多级目录"></a>第230~250行 removedirs——删除多级目录</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">removedirs</span>(<span class="params">name</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;removedirs(name)</span></span><br><span class="line"><span class="string">    Super-rmdir; remove a leaf directory and all empty intermediate</span></span><br><span class="line"><span class="string">    ones.  Works like rmdir except that, if the leaf directory is</span></span><br><span class="line"><span class="string">    successfully removed, directories corresponding to rightmost path</span></span><br><span class="line"><span class="string">    segments will be pruned away until either the whole path is</span></span><br><span class="line"><span class="string">    consumed or an error occurs.  Errors during this latter phase are</span></span><br><span class="line"><span class="string">    ignored -- they generally mean that a directory was not empty.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    rmdir(name)</span><br><span class="line">    head, tail = path.split(name)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> tail:</span><br><span class="line">        head, tail = path.split(head)</span><br><span class="line">    <span class="keyword">while</span> head <span class="keyword">and</span> tail:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            rmdir(head)</span><br><span class="line">        <span class="keyword">except</span> OSError:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        head, tail = path.split(head)</span><br></pre></td></tr></table></figure>
<p>这是一个删除多级目录的方法，而不是删除文件的。方法注释：删除一个子目录和所有空的中间目录。工作方式与rmdir类似，不同的地方是，如果最底层子目录被成功删除后，此时路径段的最右端目录也将被继续删除，直到整个路径被删除或者出现错误。后面这个阶段中的错误将被忽略——它们通常意味着目录不是空的。就是说，如果传参是”D:/ttt/eee/sss/“ 会先删除sss目录，当然前提是 “sss”目录是空的。此时再看”eee”是否为空，如果是也把”eee”删掉，不为空就此退出。</p>
<p>第11行是调用 “nt” 或 “posix”模块的底层方法 rmdir，作用就是删除一个目录，如果非空会报错。</p>
<p>第12~14行和上面makedirs函数作用一样，是将路径最后一层分隔出来，考虑路径最后有两个斜杠的情况。</p>
<p>后面在循环中依次尝试将分隔出来的 head 删掉，知道出现异常跳出循环。很好理解。</p>
<h3 id="第252-278行-renames——重命名目录或文件"><a href="#第252-278行-renames——重命名目录或文件" class="headerlink" title="第252~278行 renames——重命名目录或文件"></a>第252~278行 renames——重命名目录或文件</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">renames</span>(<span class="params">old, new</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;renames(old, new)</span></span><br><span class="line"><span class="string">    Super-rename; create directories as necessary and delete any left</span></span><br><span class="line"><span class="string">    empty.  Works like rename, except creation of any intermediate</span></span><br><span class="line"><span class="string">    directories needed to make the new pathname good is attempted</span></span><br><span class="line"><span class="string">    first.  After the rename, directories corresponding to rightmost</span></span><br><span class="line"><span class="string">    path segments of the old name will be pruned until either the</span></span><br><span class="line"><span class="string">    whole path is consumed or a nonempty directory is found.</span></span><br><span class="line"><span class="string">    Note: this function can fail with the new directory structure made</span></span><br><span class="line"><span class="string">    if you lack permissions needed to unlink the leaf directory or</span></span><br><span class="line"><span class="string">    file.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    head, tail = path.split(new)</span><br><span class="line">    <span class="keyword">if</span> head <span class="keyword">and</span> tail <span class="keyword">and</span> <span class="keyword">not</span> path.exists(head):</span><br><span class="line">        makedirs(head)</span><br><span class="line">    rename(old, new)</span><br><span class="line">    head, tail = path.split(old)</span><br><span class="line">    <span class="keyword">if</span> head <span class="keyword">and</span> tail:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            removedirs(head)</span><br><span class="line">        <span class="keyword">except</span> OSError:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line"> </span><br><span class="line">__all__.extend([<span class="string">&quot;makedirs&quot;</span>, <span class="string">&quot;removedirs&quot;</span>, <span class="string">&quot;renames&quot;</span>])</span><br></pre></td></tr></table></figure>
<p>注释是说: 创建必要的目录，并删除所有空的。类似于重命名，不同的是本函数会首先尝试创建使新路径名有效所需的中间目录。在重命名之后，与旧名称最右边路径段对应的目录将被删除，直到把旧的路径都删完或找到一个非空目录。注意:如果您缺乏断开子目录或文件链接所需的权限，那么在创建新目录结构时，此函数可能会失败。</p>
<p>还是直接看代码好理解一点，第16~18行，先判断新目录的倒数第二层路径是否存在，如果不存在就创建。所以这个函数不仅支持”D:/ttt/eee/ –&gt; D:/ttt/sss” 这种只改最后一层子目录的形式，也支持 “D:/ttt/eee/ –&gt; D:/111/222” 这种同时重命名多级目录的形式，要不说是超级版本呢。只是要注意，不管哪种形式，原来的目录为空的话会被删除，所以这个函数不能用于新建目录，新建目录还是用makedirs吧。 </p>
<p>下面还是调用 “nt”或”posix”里的方法，进行重命名，前面已经把新目录的上一层目录创建好了。再后面就是删除旧路径的过程，从最底层的目录开始，一层一层删过去，出现异常忽略。可以看出好多你自以为优雅的写法，只是标准库帮你把异常报错pass了而已。</p>
<p>最后把刚定义的三个函数加入到__all__里，前面说过__all__会不断进行扩充。</p>
<h3 id="第280-421行-walk——目录树生成器"><a href="#第280-421行-walk——目录树生成器" class="headerlink" title="第280~421行 walk——目录树生成器"></a>第280~421行 walk——目录树生成器</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">walk</span>(<span class="params">top, topdown=<span class="literal">True</span>, onerror=<span class="literal">None</span>, followlinks=<span class="literal">False</span></span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Directory tree generator.</span></span><br><span class="line"><span class="string">    For each directory in the directory tree rooted at top (including top</span></span><br><span class="line"><span class="string">    itself, but excluding &#x27;.&#x27; and &#x27;..&#x27;), yields a 3-tuple</span></span><br><span class="line"><span class="string">        dirpath, dirnames, filenames</span></span><br><span class="line"><span class="string">    dirpath is a string, the path to the directory.  dirnames is a list of</span></span><br><span class="line"><span class="string">    the names of the subdirectories in dirpath (excluding &#x27;.&#x27; and &#x27;..&#x27;).</span></span><br><span class="line"><span class="string">    filenames is a list of the names of the non-directory files in dirpath.</span></span><br><span class="line"><span class="string">    Note that the names in the lists are just names, with no path components.</span></span><br><span class="line"><span class="string">    To get a full path (which begins with top) to a file or directory in</span></span><br><span class="line"><span class="string">    dirpath, do os.path.join(dirpath, name).</span></span><br><span class="line"><span class="string">    If optional arg &#x27;topdown&#x27; is true or not specified, the triple for a</span></span><br><span class="line"><span class="string">    directory is generated before the triples for any of its subdirectories</span></span><br><span class="line"><span class="string">    (directories are generated top down).  If topdown is false, the triple</span></span><br><span class="line"><span class="string">    for a directory is generated after the triples for all of its</span></span><br><span class="line"><span class="string">    subdirectories (directories are generated bottom up).</span></span><br><span class="line"><span class="string">    When topdown is true, the caller can modify the dirnames list in-place</span></span><br><span class="line"><span class="string">    (e.g., via del or slice assignment), and walk will only recurse into the</span></span><br><span class="line"><span class="string">    subdirectories whose names remain in dirnames; this can be used to prune the</span></span><br><span class="line"><span class="string">    search, or to impose a specific order of visiting.  Modifying dirnames when</span></span><br><span class="line"><span class="string">    topdown is false has no effect on the behavior of os.walk(), since the</span></span><br><span class="line"><span class="string">    directories in dirnames have already been generated by the time dirnames</span></span><br><span class="line"><span class="string">    itself is generated. No matter the value of topdown, the list of</span></span><br><span class="line"><span class="string">    subdirectories is retrieved before the tuples for the directory and its</span></span><br><span class="line"><span class="string">    subdirectories are generated.</span></span><br><span class="line"><span class="string">    By default errors from the os.scandir() call are ignored.  If</span></span><br><span class="line"><span class="string">    optional arg &#x27;onerror&#x27; is specified, it should be a function; it</span></span><br><span class="line"><span class="string">    will be called with one argument, an OSError instance.  It can</span></span><br><span class="line"><span class="string">    report the error to continue with the walk, or raise the exception</span></span><br><span class="line"><span class="string">    to abort the walk.  Note that the filename is available as the</span></span><br><span class="line"><span class="string">    filename attribute of the exception object.</span></span><br><span class="line"><span class="string">    By default, os.walk does not follow symbolic links to subdirectories on</span></span><br><span class="line"><span class="string">    systems that support them.  In order to get this functionality, set the</span></span><br><span class="line"><span class="string">    optional argument &#x27;followlinks&#x27; to true.</span></span><br><span class="line"><span class="string">    Caution:  if you pass a relative pathname for top, don&#x27;t change the</span></span><br><span class="line"><span class="string">    current working directory between resumptions of walk.  walk never</span></span><br><span class="line"><span class="string">    changes the current directory, and assumes that the client doesn&#x27;t</span></span><br><span class="line"><span class="string">    either.</span></span><br><span class="line"><span class="string">    Example:</span></span><br><span class="line"><span class="string">    import os</span></span><br><span class="line"><span class="string">    from os.path import join, getsize</span></span><br><span class="line"><span class="string">    for root, dirs, files in os.walk(&#x27;python/Lib/email&#x27;):</span></span><br><span class="line"><span class="string">        print(root, &quot;consumes&quot;, end=&quot;&quot;)</span></span><br><span class="line"><span class="string">        print(sum(getsize(join(root, name)) for name in files), end=&quot;&quot;)</span></span><br><span class="line"><span class="string">        print(&quot;bytes in&quot;, len(files), &quot;non-directory files&quot;)</span></span><br><span class="line"><span class="string">        if &#x27;CVS&#x27; in dirs:</span></span><br><span class="line"><span class="string">            dirs.remove(&#x27;CVS&#x27;)  # don&#x27;t visit CVS directories</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    top = fspath(top)</span><br><span class="line">    dirs = []</span><br><span class="line">    nondirs = []</span><br><span class="line">    walk_dirs = []</span><br><span class="line"> </span><br><span class="line">    <span class="comment"># We may not have read permission for top, in which case we can&#x27;t</span></span><br><span class="line">    <span class="comment"># get a list of the files the directory contains.  os.walk</span></span><br><span class="line">    <span class="comment"># always suppressed the exception then, rather than blow up for a</span></span><br><span class="line">    <span class="comment"># minor reason when (say) a thousand readable directories are still</span></span><br><span class="line">    <span class="comment"># left to visit.  That logic is copied here.</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># Note that scandir is global in this module due</span></span><br><span class="line">        <span class="comment"># to earlier import-*.</span></span><br><span class="line">        scandir_it = scandir(top)</span><br><span class="line">    <span class="keyword">except</span> OSError <span class="keyword">as</span> error:</span><br><span class="line">        <span class="keyword">if</span> onerror <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">            onerror(error)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"> </span><br><span class="line">    <span class="keyword">with</span> scandir_it:</span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    entry = <span class="built_in">next</span>(scandir_it)</span><br><span class="line">                <span class="keyword">except</span> StopIteration:</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">except</span> OSError <span class="keyword">as</span> error:</span><br><span class="line">                <span class="keyword">if</span> onerror <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                    onerror(error)</span><br><span class="line">                <span class="keyword">return</span></span><br><span class="line"> </span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                is_dir = entry.is_dir()</span><br><span class="line">            <span class="keyword">except</span> OSError:</span><br><span class="line">                <span class="comment"># If is_dir() raises an OSError, consider that the entry is not</span></span><br><span class="line">                <span class="comment"># a directory, same behaviour than os.path.isdir().</span></span><br><span class="line">                is_dir = <span class="literal">False</span></span><br><span class="line"> </span><br><span class="line">            <span class="keyword">if</span> is_dir:</span><br><span class="line">                dirs.append(entry.name)</span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                nondirs.append(entry.name)</span><br><span class="line"> </span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> topdown <span class="keyword">and</span> is_dir:</span><br><span class="line">                <span class="comment"># Bottom-up: recurse into sub-directory, but exclude symlinks to</span></span><br><span class="line">                <span class="comment"># directories if followlinks is False</span></span><br><span class="line">                <span class="keyword">if</span> followlinks:</span><br><span class="line">                    walk_into = <span class="literal">True</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    <span class="keyword">try</span>:</span><br><span class="line">                        is_symlink = entry.is_symlink()</span><br><span class="line">                    <span class="keyword">except</span> OSError:</span><br><span class="line">                        <span class="comment"># If is_symlink() raises an OSError, consider that the</span></span><br><span class="line">                        <span class="comment"># entry is not a symbolic link, same behaviour than</span></span><br><span class="line">                        <span class="comment"># os.path.islink().</span></span><br><span class="line">                        is_symlink = <span class="literal">False</span></span><br><span class="line">                    walk_into = <span class="keyword">not</span> is_symlink</span><br><span class="line"> </span><br><span class="line">                <span class="keyword">if</span> walk_into:</span><br><span class="line">                    walk_dirs.append(entry.path)</span><br><span class="line"> </span><br><span class="line">    <span class="comment"># Yield before recursion if going top down</span></span><br><span class="line">    <span class="keyword">if</span> topdown:</span><br><span class="line">        <span class="keyword">yield</span> top, dirs, nondirs</span><br><span class="line"> </span><br><span class="line">        <span class="comment"># Recurse into sub-directories</span></span><br><span class="line">        islink, join = path.islink, path.join</span><br><span class="line">        <span class="keyword">for</span> dirname <span class="keyword">in</span> dirs:</span><br><span class="line">            new_path = join(top, dirname)</span><br><span class="line">            <span class="comment"># Issue #23605: os.path.islink() is used instead of caching</span></span><br><span class="line">            <span class="comment"># entry.is_symlink() result during the loop on os.scandir() because</span></span><br><span class="line">            <span class="comment"># the caller can replace the directory entry during the &quot;yield&quot;</span></span><br><span class="line">            <span class="comment"># above.</span></span><br><span class="line">            <span class="keyword">if</span> followlinks <span class="keyword">or</span> <span class="keyword">not</span> islink(new_path):</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> walk(new_path, topdown, onerror, followlinks)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># Recurse into sub-directories</span></span><br><span class="line">        <span class="keyword">for</span> new_path <span class="keyword">in</span> walk_dirs:</span><br><span class="line">            <span class="keyword">yield</span> <span class="keyword">from</span> walk(new_path, topdown, onerror, followlinks)</span><br><span class="line">        <span class="comment"># Yield after recursion if going bottom up</span></span><br><span class="line">        <span class="keyword">yield</span> top, dirs, nondirs</span><br><span class="line"> </span><br><span class="line">__all__.append(<span class="string">&quot;walk&quot;</span>)</span><br></pre></td></tr></table></figure>
<p>我觉得 walk 函数是这个模块看到现在最符合Python特点的方法，强大，方便，优雅。</p>
<p>首先一段很长的注释，主要介绍了出参和入参。该方法返回一个迭代器，包含一个三元元组，dirpath, dirnames, filenames。</p>
<p>dirpath 是当前遍历的目录的名字，从入参top开始(如果topdown为False的话)，字符串类型；</p>
<p>dirnames 是dirpath中的子目录的名字(路径名字)的列表，list类型；</p>
<p>filenames 是dirpath中的非目录的文件的名字(只有名字)的列表，list类型；</p>
<p>第一个入参 top 就是要遍历的文件夹，字符串类型。如果第二个可选入参 topdown 为 True 或未指定，目录是自顶向下生成的。如果 topdown 为 False ，则目录是自底向上生成的。举个例子：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 当前目录结构：</span></span><br><span class="line"><span class="comment"># ttt</span></span><br><span class="line"><span class="comment">#  ├── sss</span></span><br><span class="line"><span class="comment">#  ├    └──333.txt</span></span><br><span class="line"><span class="comment">#  ├── 111.txt</span></span><br><span class="line"><span class="comment">#  └── 222.txt</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">for</span> a, b, c <span class="keyword">in</span> os.walk(<span class="string">&#x27;ttt&#x27;</span>):</span><br><span class="line">    print(a, b, c)</span><br><span class="line"><span class="comment"># 输出：</span></span><br><span class="line"><span class="comment"># ttt [&#x27;sss&#x27;] [&#x27;111.txt&#x27;, &#x27;222.txt&#x27;]</span></span><br><span class="line"><span class="comment"># ttt\sss [] [&#x27;333.txt&#x27;]</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">for</span> a, b, c <span class="keyword">in</span> os.walk(<span class="string">&#x27;ttt&#x27;</span>, topdown=<span class="literal">True</span>):</span><br><span class="line">    print(a, b, c)</span><br><span class="line"><span class="comment"># 输出：</span></span><br><span class="line"><span class="comment"># ttt\sss [] [&#x27;333.txt&#x27;].</span></span><br><span class="line"><span class="comment"># ttt [&#x27;sss&#x27;] [&#x27;111.txt&#x27;, &#x27;222.txt&#x27;]</span></span><br></pre></td></tr></table></figure>
<p>第三个入参 onerror 是一个回调函数，默认情况下，onerror 参数未指定，此时调用 scandir 方法时，出现的错误将被忽略 (scandir 方法就是 nt 或 posix模块里的底层遍历目录的方法，这个内置方法只能遍历一层目录，所以像前面几个函数一样，walk 方法其实可以看做 scandir 方法的超级版本)。如果可选参数 onerror 被指定，它必须是一个包含一个入参的函数，因为后面的循环中如果出现OSError异常，会将OSError类型实例作为参数传给它。</p>
<p>默认情况下，walk方法不遵循符号链接跳转到它们指向的子目录。为了获得此功能，将可选参数 followlinks 设置为True。这里应该是说类似Linux系统的软连接硬连接那种，该参数用来选择是否迭代它们所指向的目录。我在Windows测试，快捷方式文件是不生效的，它会把快捷方式当成一个普通文件。</p>
<p>注意：如果为top传递的是相对路径名，不要在 walk 函数执行期间之间更改当前工作目录。walk 从不更改当前目录，并假设客户机也不更改当前目录。</p>
<p>看看代码部分：第60行将 top 参数传到 fspath 转换了一下，这个方法是在本模块后面1060行定义的，主要作用是判断传入的参数是不是字符串类型的目录名，如果不是，直接报错。fspath 实现细节后面再具体看。在一些其他语言比如C语言中，要调用一个方法和属性必须在调用出现之前进行定义，在Python这里对这个顺序要求不是很在意。</p>
<p>第61~63行，定义了三个列表，dirs用来存放遍历过程中的目录名，nondirs用来存放遍历过程中的文件的名字，walk_dirs 用来存放要遍历的子目录，用于后面继续迭代它们的子目录。</p>
<p>第65~69行注释，在没有top目录的读权限的情况下，无法获得目录中包含的文件列表。大部分情况下walk总是忽略一些异常，这样避免(比方说)在还有1000个可读目录需要访问时，因为一个小原因而崩溃。这个逻辑复制到这里。</p>
<p>第71~73行，这里是walk函数的核心部分，调用 scandir (nt或posix里的)，这个方法返回一个迭代器，包含入参目录下的所有DirEntry类型的子目录和文件，DirEntry是nt(或posix)模块定义的一个类，包含一个目录或文件的基本信息。这里将其返回的迭代器赋值给scandir_it。这里的注释是提醒scandir是在前面对 nt 模块或者 posix 模块 import * 时引入的。我怀疑os模块不是一个人完成的，因为上面几个函数在调用别的模块的方法时，就没有这种提示，还需要自己去找一些方法或属性的出处。</p>
<p>第74~77行，如果第一级目录再进行遍历的时候就出现了 OSError 类型的异常，就调用回调函数 onerror 方法处理(如果有定义的话)，然后直接返回。</p>
<p>79行开始进入循环。第81~89行取出迭代器 scandir_it 的下一个元素，也就是 top 目录下的第一个子目录或者文件，如果迭代器被迭代完了，就pass。这里同遇到异常，会和74到77行的处理方法一样。</p>
<p>第91~101行，判断取出的第一个元素是不是目录，is_dir 方法是DirEntry类型实例的一个方法。（还记得吗，73行scandir_it = scandir(top)返回的迭代器，里面包含的实例是DirEntry类型的，在83行entry = next(scandir_it)取出）。如果该元素是目录就加到列表 dirs 里，否则加到列表 nondirs 里。</p>
<p>此时，如果 topdown 为True 或未指定，循环体就执行完了。接下来继续遍历，直到把第一层目录下的子目录和文件都分类保存在两个列表里（这里应该想到后面肯定会有递归）。如果topdown 参数为False，而且当前元素是个目录(这里来看链接也算目录)，第103~119行，这里又要考虑followlinks参数，如果为True，就将当前元素的路径加入到列表 walk_dirs 里，如果为Flase或者未指定，当前元素是链接的话就不加入待迭代目录列表walk_dirs，不是链接(普通目录)就加入到walk_dirs。</p>
<p>第121~134行，如果是自顶向下生成的，这时候就可以 yield walk这个迭代器的第一个元素了。然后准备第二个元素(这么说可能不对) ：将 top 和 dirs 里的路径名字组合，形成新的路径名，在for循环中调用walk就完成第二层目录的迭代，这样递归下去就可以遍历到所有的子目录，递归的跳出点就在两个return那里。</p>
<p>第135~140行，如果是自底向上生成的，就先不yield，先去处理待迭代的列表里的路径，层层递归，这样就会先 yield 最低层目录，再 yield 上层的目录。</p>
<p>最后，将walk函数加入到__all__列表里。总的来说，walk这个函数利用Python的迭代器，设计的很巧妙，都看完了让我写也写不出来。</p>
<h3 id="未完待续……"><a href="#未完待续……" class="headerlink" title="未完待续……"></a><strong>未完待续……</strong></h3><p>今天写了一下午不知道为啥没保存上，晚上回来打开进度又回到上次写的那里，一下午白干了，刚刚才又凭着记忆重写回来，感觉有的地方跟第一遍用词不一样了。</p>
<p>os模块源码共1115行，到现在才写到421行，这篇文章已经太长了，我决定分成两篇文章，后续写在<a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/108492310">彻底弄懂Python标准库源码（二）—— os模块（续）</a>里。</p>
<p>这篇文章是我看着源码凭自己理解写的，里面有一些自己的臆断，有看到错误的朋友，麻烦帮忙指出来更正，感谢！</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>版权声明： </strong>
          
          本博客所有文章除特别声明外，著作权归作者所有。转载请注明出处！
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://example.com/2020/06/15/%E5%BD%BB%E5%BA%95%E5%BC%84%E6%87%82Python%E6%A0%87%E5%87%86%E5%BA%93%E6%BA%90%E7%A0%81%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20os%E6%A8%A1%E5%9D%97/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/os/" rel="tag">os</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/python/" rel="tag">python</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/" rel="tag">编程语言</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2020/07/25/%E5%BD%BB%E5%BA%95%E5%BC%84%E6%87%82%20Python3%E4%B8%AD%E5%85%A5%E5%8F%82%E9%87%8C%E7%9A%84%E6%98%9F%E5%8F%B7%E7%9A%84%E4%BD%9C%E7%94%A8/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            彻底弄懂 Python3中入参里的*号的作用
          
        </div>
      </a>
    
    
      <a href="/2020/05/25/%E5%BD%BB%E5%BA%95%E5%BC%84%E6%87%82Python%E6%A0%87%E5%87%86%E5%BA%93%E6%BA%90%E7%A0%81%EF%BC%88%E9%9B%B6%EF%BC%89%E2%80%94%E2%80%94%20%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">彻底弄懂Python标准库源码（零）—— 学习计划</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "zy6yBRj9KkWO2XxsT94n1DIW-gzGzoHsz",
    app_key: "auroBE2PQXkQ05CLi30SFv92",
    path: window.location.pathname,
    avatar: "monsterid",
    placeholder: "给我的文章加点评论吧~",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
   
  
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2020-2021
        <i class="ri-heart-fill heart_icon"></i> 杰克小麻雀
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/favicon.ico" alt="半亩方塘"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/img/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/img/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->


<script src="/js/clickBoom2.js"></script>


<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
  </div>
</body>
<script type="text/javascript">
if(!/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)){
  document.write('<script type="text/javascript" src="/js/FunnyTitle.js"><\/script>');
  document.write('<script type="text/javascript" src="/js/snow.js"><\/script>');
}
</script>
</html>